package com.tofacebook.proxy.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Arrays;

//需要使用@Aspect注解，标志该类是面向切面的类
@Aspect
//需要将该类通过注解的方式注入到Spring IOC容器里面
//该类是一个普通得组件 那么需要使用 @component
@Component
public class EmpAspects {
    private static final Logger log = LoggerFactory.getLogger(EmpAspects.class);
    /**
     * 前置通知：使用@Before注解标识，在被代理的目标方法前执行
     * 返回通知：使用@AfterReturning注解标识，在被代理的目标方法成功结束后执行（寿终正寝）
     * 异常通知：使用@AfterThrowing注解标识，在被代理的目标方法异常结束后执行（死于非命）
     * 后置通知：使用@After注解标识，在被代理的目标方法最终结束后执行（盖棺定论）
     * 环绕通知：使用@Around注解标识，使用try...catch...finally结构围绕整个被代理的目标方法，
     * 包括上面四种通知对应的所有位置
     *
     * 在代理实现类的基础  使用spring 中aop的思维来替换代理实现类的逻辑
     *
     * 说白了就是在调用目标对象中的方法，
     * 我们现在不需要使用jdk中的代理方式，而是用spring aop中前置通知...环绕通知
     * execution  ：表达式, 切入点在哪里 一般都写我们类中方法或者使用通配符来匹配
     *  public Emp queryEmpByEmpId(Long empId)
     */
    //@Before("execution(* com.tofacebook.proxy.service.impl.EmpServiceImpl.queryEmpByEmpId(Long))")

    /**
     * 星号是通配符匹配报名，类名，方法
     * 点点  ：参数列表
     */
    //@Before("execution(* com.tofacebook.proxy.service.impl.*.*(..))")
    //@Before("execution(* com.tofacebook.proxy..*.*(..))")
    //换成公共切入点表达式
    @Before("pointCut()") //前置通知方法
    public void beforeMethod(){
        log.info("调用目标对象中的方法之前需要先执行beforeMethod()方法：这叫前置方法");
    }
    //切入点公共的表达式
    @Pointcut("within(com.tofacebook.proxy..*)")
    public void pointCut() {
    }

    //@After("execution(* com.tofacebook.proxy.service.impl.EmpServiceImpl.queryEmpByEmpId(Long))")
    @After("pointCut()")  //返回通知方法
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();//切入点方法就是queryEmpByEmpId()
        log.info("Logger-->finally里面执行：后置通知，方法名："+methodName);
    }
    @AfterReturning(value = "pointCut()",returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint, Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->调用目标方法后要执行返回通知，方法名："+methodName+",结果："+result);
    }
    @AfterReturning(value = "pointCut()",returning = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->异常通知，方法名："+methodName+"，异常："+ex);
    }

    /**
     * 环绕通知
     * @param joinPoint
     * @return
     */
    @Around("pointCut()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        Object result = null;
        try {
            System.out.println("环绕通知-->目标对象方法执行之前");
            //目标对象（连接点）方法的执行
            result = joinPoint.proceed();
            System.out.println("环绕通知-->目标对象方法返回值之后"+result);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知-->目标对象方法出现异常时");
        } finally {
            System.out.println("环绕通知-->目标对象方法执行完毕");
        }
        return result;
    }

}
